#include "sourceField.hpp"
#include "vtkFunctions.hpp"
#include "vtkFunctions_import.hpp"
#include "exceptions.hpp"

namespace hms::vtk
{

namespace internal {

template<typename SolverType, typename ... Ts>
void assignSourceField(
	SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::SourceFieldName fieldName,
	Uniformity isUniform,
	Ts&& ... args
){
	using MeshType = typename SolverBase<SolverType>::MeshType;

	#define ASSIGN_SOURCE_FIELD(UNIFORMITY) \
	solver.sourceField(fieldName) = \
		SourceField<UNIFORMITY, MeshType>{ std::forward<Ts>(args) ... }

	if (isUniform)
		ASSIGN_SOURCE_FIELD(Uniform);
	else
		ASSIGN_SOURCE_FIELD(NonUniform);

	#undef ASSIGN_SOURCE_FIELD
}

}

template<typename SolverType>
void readSourceField_noUpdate(
	const xml::XMLElement* fieldElem,
	SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::SourceFieldName fieldName,
	Uniformity isUniform,
	Index nComponents
){
	ArrayXXs values;
	resizeFieldValues( values, nComponents, solver.mesh(), isUniform );
	textToFieldValues(fieldElem, values, solver.mesh(), isUniform);

	internal::assignSourceField(
		solver, fieldName, isUniform, solver.mesh(), std::move(values)
	);
}

template<typename SolverType>
void readSourceField_timeSeries(
	const xml::XMLElement* fieldElem,
	SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::SourceFieldName fieldName,
	Uniformity isUniform,
	Index nComponents
){
	bool useInterpolation {
		getAttributeValue<bool>(fieldElem, "useInterpolation")
	};
	internal::assignSourceField(
		solver, fieldName, isUniform,
		solver.mesh(),
		readTimeSeries( fieldElem, nComponents, solver.mesh(), isUniform ),
		solver.time(),
		useInterpolation
	);
}

template<typename SolverType>
void readSourceField_function(
	const xml::XMLElement* fieldElem,
	SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::SourceFieldName fieldName,
	Uniformity isUniform,
	Index nComponents
){
	internal::assignSourceField(
		solver, fieldName, isUniform,
		solver.mesh(),
		readFunctions( fieldElem, nComponents, true ),
		solver.time(),
		solver.zField()
	);
}

template<typename SolverType>
void readSourceField(
	const xml::XMLElement* fieldElem,
	SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::SourceFieldName fieldName
){
	// using MeshType = typename SolverBase<SolverType>::MeshType;
	using U = UpdateMethod;

	auto nComponents { getAttributeValue<Index>(fieldElem, nComponentsName) };
	auto isUniform   { static_cast<Uniformity>(
		getAttributeValue<bool >(fieldElem, "isUniform")
	)};
	const char* updateMethodStr {
		getAttributeValue<const char*>(fieldElem, "updateMethod")
	};
	/* this should already get caught by getAttributeValue() */
	if ( !updateMethodStr ){
		throw ParseError(
			"Attribute \"updateMethod\" not found in\n" + nodeString(fieldElem)
		);
	}
	std::optional<U> updateMethod { enum_cast<U>(updateMethodStr) };
	if ( !updateMethod ){
		throw ParseError(
			"Update method \""s + updateMethodStr + "\" unknown!\n"
			+ nodeString(fieldElem)
		);
	}
	switch ( updateMethod.value() ){
		case U::none:
			readSourceField_noUpdate(fieldElem, solver, fieldName, isUniform, nComponents);
			break;
		case U::timeSeries:
			readSourceField_timeSeries(fieldElem, solver, fieldName, isUniform, nComponents);
			break;
		case U::function:
			readSourceField_function(fieldElem, solver, fieldName, isUniform, nComponents);
			break;
		case U::callable:
			break;
	}
}

template<typename SolverType>
void readSourceFields(
	const xml::XMLDocument* doc,
	SolverBase<SolverType>& solver
){
	using S = SolverBase<SolverType>;
	using SFName = typename S::SourceFieldName;

	const xml::XMLElement* sourceElem { getChildNodeByName(doc, sourceNode) };

	for ( int i{0}; i < S::nSourceFields ; ++i ){
		auto fieldName { static_cast<SFName>(i) };
		
		if ( !solver.useSourceField(fieldName) )
			continue;

		std::string fieldNameStr { magic_enum::enum_name(fieldName) };

		const xml::XMLElement* fieldElem { getChildNodeByAttributeValue(
			sourceElem, sourceChild, "Name", fieldNameStr
		) };

		readSourceField(fieldElem, solver, fieldName);
	}
}

} // namespace hms::vtk
